Skip to content

fix(antigravity): comprehensive fix for user skills, bypass permissions, subagent execution - #3890

Open
KhoiFishGST wants to merge 5 commits into
omnigent-ai:mainfrom
KhoiFishGST:agy-harness-support
Open

fix(antigravity): comprehensive fix for user skills, bypass permissions, subagent execution#3890
KhoiFishGST wants to merge 5 commits into
omnigent-ai:mainfrom
KhoiFishGST:agy-harness-support

Conversation

@KhoiFishGST

@KhoiFishGST KhoiFishGST commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Running agy through Omnigent lost most of what agy was doing. The terminal showed
the real session; the web UI showed a subset of it. This brings the two to parity.

Everything here was found by reading live agy connect-RPC traffic and verified
against real sessions. The recorded frames are checked in as fixtures — including
six that capture a wire shape the suite had never covered, which is why the
tool-call bug below could ship green.

ELI5. agy reports what it is doing over two different channels. We read the fast
one, which leaves fields out, while the code assumed the complete one. So the web UI
quietly dropped tool calls, sub-agents, and parts of replies.

                          GetCascadeTrajectorySteps   StreamAgentStateUpdates
                          (poll — complete)           (live — what we read)
metadata.toolCall              present                    STRIPPED
plannerResponse.toolCalls      present                    STRIPPED
                          └─ the mapper was built     └─ production runs here
                             against this shape

Nine fixes, each independently reviewable in the diff:

Area Symptom Cause
Plugins agy plugin list empty under Omnigent, fine outside isolated --gemini_dir never seeded
Skills slash menu offered Claude's skills no antigravity skill-source family
Permissions --dangerously-skip-permissions unreachable from the UI no capability declared
Sub-agents each spawn forked a duplicate top-level session rotation detector read children as /clear
Discovery a session could bind another agy's RPC port ownership unverified after StartCascade
Deps lsof shelled out for port attribution undeclared, absent from many images, no Windows
Streaming replies duplicated and truncated every delta stamped "index": 0
Tool calls 611 tool outputs, 0 invocations recorded stream strips toolCall / toolCalls
Sub-agents their work invisible; Agents rail empty child cascades never mirrored

The last two are the substantial ones.

Tool calls. The stream omits exactly the two fields tool correlation depended on
— each embeds a thinkingSignature blob, and the typed body (runCommand,
viewFile, …) already describes the call. So invocations were never emitted; known
result types fell back to a FIFO allocator whose queue was necessarily empty and
minted _orphan_N ids; and unknown types (view_file, invoke_subagent) failed the
tool-result test and were dropped silently. Across 10 recorded conversations: 611
outputs, 0 invocations.
Both items now derive from the result step — which both
shapes deliver in full — keyed on its own (trajectory, step) identity, so a
stream→poll fallback cannot re-key a pair. Classification moved to
metadata.toolAction, present on exactly the tool steps in both shapes and in
year-old fixtures, so an agy tool type this mapper has never seen still reaches the UI.

Sub-agents. agy runs each as its own cascade and names its id, role and type on
the parent's INVOKE_SUBAGENT step. Omnigent already read that metadata — only to
ignore those cascades, since a working sub-agent is always more recently active
than the parent idling behind it and otherwise looks like a /clear. Each child now
gets a child session and a mirror loop reusing the same mapper. One subtlety:
invoke_subagent is fire-and-forget — its step reaches DONE while the child runs on
for minutes (measured: a child worked 23s past its spawning step's completedAt and
ran 35 steps) — so each mirror ends on its own child's turn closing, with agy's
run status as the backstop for a turn that never closes cleanly.

Server-side this adds a third *_subagent_start event alongside claude's and codex's.
It needs less machinery than either: agy's cascade id is already stable and unique, so
the title "<role>:<cascade id>" is both the idempotency key and correctly parsed by
the Agents rail's existing first-colon split — no display helper, no label-scan lookup.

Test Plan

730 passed, 1 skipped across the antigravity selection; pre-commit clean.
28 files: +3515 / −633 — 1297 production, 1768 test, 274 fixture, 176 web.

Automated:

  • 6 new stream-projection fixtures, verbatim live frames. The suite had no
    coverage of that shape at all, which is why every test passed while production
    lost data.
  • Mapper: the pair emitted per tool step, recovered view_file, agy's own tool
    names, identical call ids from both RPC shapes, the planner staying silent on
    both, and a sweep asserting no fixture can mint an orphan id.
  • Reader: sub-agent registration once across repeated frames, the role/type/tool-call
    payload, a rejected registration starting no mirror, a child's steps landing in the
    child session, the mirror outliving the spawning step, a stalled child closed
    from agy's run status, and a quiet-but-running child not closed.
  • Server: the minted child row and its rail fields, idempotency, a missing
    cascade_id rejected, and a colon inside a model-authored role.

Each regression test was observed failing for the right reason before its fix, and
the load-bearing ones re-checked by reverting the fix (restoring "index": 0 fails
the delta tests; restoring stop-on-parent-settle fails the keeps-polling test).

Manual, against live agy sessions:

  • agy plugin list A/B inside vs outside Omnigent; the /skills panel listing agy's
    own skills.
  • Live SSE capture confirming 0 stale replays after the delta-index fix.
  • Replaying real conversations through the new mapper: 18 tool steps → 18 complete
    pairs, 0 orphans, 0 unpaired, stream and snapshot agreeing on every id and name

    (was 8 orphaned, 3 correct, 7 dropped, 0 invocations).
  • Replaying the real sub-agent cascades: children that had recorded 1 item each now
    mirror their full transcripts (2 messages + 16 tool pairs, 3 + 43, 2 + 8) and each
    closes with a clean running → idle.

Demo

2026-08-03.07-55-57.mp4

Type of change

  • Bug fix
  • Feature
  • UI / frontend change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • Manual verification completed

Coverage notes

The manual verification above needs a live agy process and a real RPC port, so it is
not automated; the unit and integration tests pin each behaviour from recorded
frames. The fixtures are real captures, sanitised only for paths.

Two things reviewers should weigh:

  1. A third copy of the subagent-start handler. claude's and codex's are already
    near-identical, and this follows them rather than generalising all three — a
    refactor that would touch two working harnesses. Happy to do that instead if
    preferred.
  2. Sub-agent tool cards appear at completion, not at dispatch. The stream does
    deliver PENDING/RUNNING frames, so a live in-progress card is possible; it was left
    out to keep this change to the data loss. Easy follow-up.

Changelog

agy sessions now mirror tool calls and sub-agents into the web UI, and no longer
duplicate or truncate replies

@github-actions github-actions Bot added the size/XL Pull request size: XL label Aug 2, 2026
@github-actions
github-actions Bot requested a review from SabhyaC26 August 2, 2026 13:06
@KhoiFishGST

Copy link
Copy Markdown
Contributor Author

This was a comprehensive fix for agy as I have been trying to get agy harness to work well (like claude code). This does everything that claude code does well for me (subagent calling, skills, and agents listed in tab in the webui).

I will provide a video demo soon (tomorrow) and also check issues reported so far and tag them here.

@PattaraS for visibility.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@KhoiFishGST This PR is a Bug fix, Feature, or UI / frontend change but the Demo section is missing or only contains a placeholder.

These change types require a screenshot or screen recording so reviewers can see the new behaviour without checking out the branch. Please update the Demo section with:

  • A screenshot or screen recording of the change, or
  • A link to a hosted video or GIF showing the new behaviour.

Use N/A only when the change has no user-visible effect whatsoever (e.g. a pure refactor or test-only change). If that's the case, uncheck the relevant type box and check Refactor / chore or Test / CI instead.

@github-actions github-actions Bot added the needs-demo PR needs a demo screenshot or recording label Aug 2, 2026
@KhoiFishGST KhoiFishGST changed the title fix(antigravity): make the agy harness usable from the web UI fix(antigravity): comprehensive fix for user skills, bypass permissions, subagent execution Aug 2, 2026
@KhoiFishGST

Copy link
Copy Markdown
Contributor Author
2026-08-03.07-55-57.mp4

Here's a video of it working, agents running and listed under the agents tab.

There's one approval message that comes up that gets triggered somehow. The agents run anyway, and once you hit approve it goes away. I can look at this as a follow-up; this diff is getting much too large as it is.

Let me know if you guys need anything from me. This makes my agy workflow great; I can work in the browser with antigravity, with subagents!

Running agy through Omnigent lost most of what agy was doing. This
brings the web UI to parity with what the terminal already showed.

Every fix below was found by reading live agy RPC traffic and verified
against real sessions; the recorded frames are checked in as fixtures.

**Plugin skills were missing.** An omnigent-spawned agy gets an isolated
`--gemini_dir`, and nothing seeded the user's plugins into it, so
`agy plugin list` was empty under Omnigent while identical outside it.
The bridge now symlinks `config/plugins` and copies `import_manifest.json`.

**The slash menu offered Claude's skills.** The skill-source registry
had no antigravity family, so agy sessions fell through to the
claude-native provider. agy now has its own five sources, with plugin
skills namespaced `<plugin>:<skill>` and enabled only when `plugin.json`
is present.

**`--dangerously-skip-permissions` was unreachable.** claude-code exposes
its bypass in the new-chat dialog; agy had no equivalent, so the flag
could only be set by hand-editing launch args. Added as a capability with
the same danger banner.

**Sub-agents forked duplicate top-level sessions.** agy spawns each
sub-agent as its own cascade, and a working sub-agent is always more
recently active than the parent idling behind it — so the rotation
detector read every spawn as a `/clear` and dragged the pane onto the
child. Children are now identified by `trajectoryMetadata` and skipped.

**Cold start could bind a stranger's agy.** With several agy processes
alive, a session could attach to another one's RPC port and mirror its
conversation. Ownership is now confirmed after `StartCascade`. Port
attribution also moved from shelling out to `lsof` — an undeclared
dependency absent from many images, and unavailable on Windows — to
psutil, which is already a dependency, with a `/proc/net/tcp` fallback.

**Replies duplicated and truncated.** The streaming reader stamped a
constant `"index": 0` on every text delta, and the server discards any
chunk whose index does not advance — so the first chunk rendered, the
rest were dropped, and the unretired buffer replayed to later
subscribers. Deltas now carry a real index, and the live block is closed
on both the stream and poll paths.

**No tool call was ever mirrored.** agy serves each step at two
fidelities: the snapshot RPC carries `metadata.toolCall` and
`plannerResponse.toolCalls`, while the live stream strips both (each
embeds a `thinkingSignature` blob). The mapper was built against the
snapshot, so streamed turns recorded 611 tool outputs against 0
invocations — naked result blobs, most keyed to invented `_orphan_N`
ids, with `view_file` and `invoke_subagent` results dropped entirely.
Both items now derive from the result step, which both shapes deliver in
full, keyed on its own `(trajectory, step)` identity so a stream->poll
fallback cannot re-key a pair.

**Sub-agent work was invisible.** agy names each sub-agent's cascade,
role and type on the parent's `INVOKE_SUBAGENT` step, but nothing
mirrored them, so a four-reviewer dispatch showed one opaque tool call
and an empty Agents rail. Each child now gets a child session and a
mirror loop. `invoke_subagent` is fire-and-forget — its step reaches DONE
while the child runs on for minutes — so each mirror ends on its own
child's turn closing, with agy's run status as the backstop for a turn
that never closes.

Test plan:
- 730 passed, 1 skipped across the antigravity selection; pre-commit clean.
- 6 stream-projection fixtures are verbatim live frames — the shape that
  had no coverage, which is why the tool-call bug shipped.
- Every fix verified end-to-end against a live agy: `agy plugin list`
  A/B, the `/skills` panel, live SSE captures for the delta index, and a
  replay of the real conversations for tool calls (18 tool steps -> 18
  complete pairs, both RPC shapes agreeing) and sub-agents (children that
  had recorded 1 item each now mirror their full transcripts and close).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Khoi Nguyen <khoifish@goodstorytime.com>
The E2E UI Required gate rejected the PR: the new-chat dialog gained
agy's permission control with only Vitest coverage under web/, and no
Playwright test exercising it. The gate is right — this is the toggle
that arms `--dangerously-skip-permissions`, and the repo requires a UI
test for user-facing UI changes.

Two tests, driving a real browser against the stubbed landing picker:

* arming the bypass raises the red danger banner and rides along to
  `POST /v1/sessions` as
  `terminal_launch_args: ["--dangerously-skip-permissions"]`;
* leaving it alone sends NO launch args, so a session cannot silently
  inherit the bypass the user never chose.

The banner assertion is the point of the first test as much as the flag
is. agy fires no pre-tool hook, so once the bypass is armed Omnigent
cannot re-gate individual tools — the warning is the only thing between
the user and an agent that edits any file and runs any command without
asking. The test also asserts the banner is ABSENT before opting in, so
it cannot decay into permanent furniture that users learn to ignore.

Both reuse the module's existing `_antigravity_native_agents_body`
stub rather than adding a second one.

Test plan:
- Both pass in a real chromium run (2 passed), and the whole
  `test_start_session.py` file passes (22 passed).
- Each assertion verified to bite: emptying the flag's `args` fails the
  launch-args assertion, and suppressing the banner fails the visibility
  assertion.
- pre-commit clean.

Signed-off-by: Khoi Nguyen <khoifish@goodstorytime.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@KhoiFishGST

Copy link
Copy Markdown
Contributor Author

I merged tip of main into the branch and resolved requested tests (e2e UI test ran and passed). Everything green.

Diff is large but tests are about 62% of the diff. About ~1k runtime lines impacted (~38% of the diff).

Tested on my end and things still work as expected. Let me know if you need anything else. Excited to see this thing land!

@SabhyaC26 @PattaraS

@KhoiFishGST

Copy link
Copy Markdown
Contributor Author

Hey all! I would love some feedback for this, and would happily make changes, split up the changes etc. if required. Please let me know.

@dhruv0811
dhruv0811 requested review from TomeHirata and removed request for SabhyaC26 August 4, 2026 17:53
@dhruv0811 dhruv0811 assigned TomeHirata and unassigned SabhyaC26 Aug 4, 2026
@PattaraS

PattaraS commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

/review

@PattaraS PattaraS left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff plus the surrounding code the change leans on (inflight_text.py, _detect_rotated_cascade, the codex sibling handler, the harness alias tables) and the six new fixtures.

The two load-bearing fixes hold up, and I checked the wire-shape premise rather than taking the description's word for it: in stream_*_done.json the tool steps carry metadata.toolAction with an empty toolCall block, and stream_planner_tool_call.json has no toolCalls — so keying the pair on the invocation really did lose every streamed tool call, and toolAction really does discriminate (both planner fixtures lack it). The delta-index bug is likewise real: inflight_text.py:608 drops any chunk whose index <= last_index, so a constant 0 silenced everything after the first, the message never saw final, never retired, and got replayed truncated to later subscribers. len(forwarded) is monotonic by construction. Nice work on _close_planner_delta_stream mirroring the mapper's modifiedResponse-over-response precedence — that's what keeps the buffer byte-equal to the committed item.

Five comments inline. One is a real defect (the psutil floor), one is a design question I'd want answered before merge (the skills truth-source), and three are minor.

On your two flagged concerns: the third subagent-start handler is genuinely thinner than codex's — the title is the idempotency key, and _find_subagent_child_by_title already existed to support it — so following the pattern over refactoring two working harnesses looks right to me. Deferring live in-progress tool cards is a reasonable scope line.

Process note, not a blocker: four of the nine fixes touch disjoint files and had no coupling to the rest (psutil/lsof, the skills provider, the permissions toggle, plugin seeding). Those could have shipped as their own PRs and left a much smaller surface for the two hard ones. Not worth re-cutting now — the live-agy verification is the expensive part and a re-split makes you redo it per slice — but worth reaching for next time a change spans this many independent causes.

Reviewed with Claude Code.

"""
try:
conns = psutil.Process(pid).net_connections(kind="tcp")
except (psutil.Error, OSError):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The >=5.9 floor permits a version where this path always fails.

Process.net_connections() was added in psutil 6.0.0 (2024-06-18) as the rename of Process.connections(); pyproject.toml:51 pins psutil>=5.9,<8. On 5.9 the call raises AttributeError, which is caught by neither psutil.Error nor OSError — so it propagates instead of falling back to lsof, making discovery worse than before on that floor rather than better.

The lockfile resolves to 7.2.2, so CI and normal installs are fine; it's --resolution lowest or a downstream consumer with an older pin that breaks. Bump the floor to >=6 (matching the API you actually call) or widen the except to include AttributeError.

Comment thread omnigent/spec/skill_sources.py Outdated

:param ctx: Session discovery context. ``home`` is the real user home: agy
runs under a bridge-owned ``--gemini_dir`` whose plugins are linked back
to the real tree, so the real home is the truthful source either way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The menu can list skills agy won't resolve.

This says the real home is truthful "either way," which holds for plugins — _seed_isolated_agy_plugins symlinks config/plugins back to the real tree — but not for the other three sources _agy_skill_dirs enumerates. Global (antigravity-cli/skills), Shared (~/.gemini/skills) and builtins are read from the real home here, yet agy runs under the bridge-owned --gemini_dir (orchestration.py:4343) where nothing seeds them.

So a skill in ~/.gemini/antigravity-cli/skills/ would appear in /skills and then fail to expand when sent to agy as plaintext — which is the exact failure mode pi_host_skills exists to avoid ("listing anything would risk surfacing a command the harness can't run").

test_antigravity_provider_surfaces_all_five_agy_sources asserts the current behaviour, so I take it this is deliberate — what's the reasoning? Either seed the other three alongside plugins, or narrow the provider to what the isolated Gemini dir actually contains.

# Sub-agent mirrors poll until their parent step settles; a
# reader teardown mid-flight must not leave them polling a
# cascade whose agy is going away.
*state.subagent_mirrors.values(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grandchild mirrors are unreachable at teardown.

child_state in _mirror_subagent_cascade gets its own subagent_mirrors, and the _process_committed_step_maybe_mirror_subagents call inside that loop will populate it if a child spawns its own sub-agent. This drain only walks the parent's dict, so a depth-2 mirror holds no reachable reference and keeps polling after the reader is gone.

You already read nestingDepth in _summary_is_child_trajectory — if agy caps nesting at 1 this is unreachable and worth a comment saying so; if it doesn't, the drain needs to recurse into each child's mirrors.

Comment thread omnigent/antigravity_native_reader.py Outdated
)
await _post_event(client, child_session_id, _status_event(_STATUS_IDLE))
return
quiet_polls = 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reset makes the backstop unbounded rather than one-shot.

When the child has been quiet for 60 polls but agy reports it not-idle, zeroing quiet_polls restarts the whole window — so a child agy never marks idle re-checks every 60s for the life of the session.

The direction is right (a wrong "idle" truncates a transcript, which is the worse failure), but _SUBAGENT_QUIESCENT_POLLS' "generous" framing reads as a single grace period rather than an indefinite re-check loop. Worth saying so in the comment, or backing the interval off after the first failed check.

continue
if summary.get("trajectoryType") != _TRAJECTORY_TYPE_CASCADE:
continue # never rotate to a subagent/child trajectory
continue # never rotate to a non-cascade trajectory

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comments left behind — including the exact claim this PR disproves.

The module-level comment at :184 still says a subagent/child trajectory "carries a different trajectoryType", which _summary_is_child_trajectory documents as byte-identical to a real root. _detect_rotated_cascade's docstring bullet ("Consider ONLY root cascades … a subagent/child trajectory is never a rotation target") likewise still attributes child-exclusion to the trajectoryType check, without mentioning the new metadata test now doing that work.

Both would lead the next reader to think the type check is load-bearing for child exclusion when this line is really just filtering non-cascade types.

@omnigent-ci

omnigent-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Polly AI Review

Blocking issues

1. Missing imports → NameError at runtime on the new sub-agent event path. The PR wires up external_antigravity_subagent_start end-to-end and adds its symbols to __all__ lists, but never adds them to the import blocks of the three consuming modules. These modules use explicit named imports (no import *), so the references resolve to nothing:

  • omnigent/server/routes/sessions/routes_events.py — uses _EXTERNAL_ANTIGRAVITY_SUBAGENT_START_TYPE (in the excluded-event tuple and the body.type == … branch) and _persist_external_antigravity_subagent_start, but neither is added to the from …common import (…) / from …orchestration import (…) blocks (which do explicitly import the _CODEX_* equivalents).
  • omnigent/server/routes/_sessions/orchestration.py_persist_external_antigravity_subagent_start calls _antigravity_subagent_title, _antigravity_subagent_labels_from_body, and _create_and_publish_antigravity_child, none of which are added to its from …helpers import (…) block.
  • omnigent/server/routes/_sessions/helpers.py_antigravity_subagent_title / _antigravity_subagent_labels_from_body / _create_and_publish_antigravity_child reference the new _ANTIGRAVITY_NATIVE_SUBAGENT_* constants, which are defined in common.py but not added to helpers' from …common import (…) block.

I verified this by applying the diff to a scratch checkout and running an AST scan of each module's resolvable names: all six antigravity symbols report unresolved, while the _CODEX_* controls resolve. There are no star-imports to cover them. Net effect: the first POST …/events with external_antigravity_subagent_start raises NameError, so sub-agent mirroring — one of the two headline fixes — never works, and the PR's own added integration tests (test_external_antigravity_subagent_start_*, asserting HTTP 202) would fail. Add each symbol to the corresponding import block (matching how the codex equivalents are already imported).

Security vulnerabilities

None introduced. The --dangerously-skip-permissions toggle is an intentional, explicit opt-in gated behind a persistent danger banner, is only appended to terminal_launch_args when the non-default value is selected, and flows through the existing _validate_terminal_launch_args boundary (flat list of strings, bounded count/length, no internal-wiring keys). The plugin symlink seeding is best-effort and links back to the user's own real ~/.gemini tree.

Non-blocking notes

  • Grandchild mirror-task leak (antigravity_native_reader.py, _mirror_subagent_cascade): a child's steps run through _process_committed_step, which unconditionally calls _maybe_mirror_subagents, so a nested INVOKE_SUBAGENT registers grandchild tasks into the child's child_state.subagent_mirrors. That dict is never drained — _mirror_subagent_cascade has no try/finally, and parent teardown only cancels the parent's mirrors. If agy can nest sub-agents, grandchild pollers survive teardown; confirm agy's nesting behavior and add a finally that cancels child_state.subagent_mirrors.
  • Closing planner delta can be dropped on a moderation rewrite (_close_planner_delta_stream + output_text_delta_event): the closer sends index=len(forwarded), but _emit_partial_delta re-anchors prefixes[idx] even on a non-extending frame, so forwarded can shrink. If the committed modifiedResponse is a shorter, non-prefix post-moderation rewrite, the closer's index can be ≤ last_index and inflight_text drops it (index <= message.last_index), leaving the message un-retired — the exact duplicate-replay this PR fixes, in the moderation corner. Still strictly better than the old hardcoded index: 0; a monotonic per-message counter would fully close it.
  • _arguments_from_body prefix match is order-fragile (antigravity_native_steps.py): startswith(wanted) returns the first body key in dict-insertion order, so a body carrying suffix-variant siblings could surface the wrong value. Current fixtures don't collide, so it's latent; an exact-then-prefix match would harden it.
  • Duplicate child row under concurrent redelivery (_create_and_publish_antigravity_child): idempotency relies on a (parent, title) check that the store enforces via a pre-insert SELECT, not a DB unique index, so two simultaneous redeliveries can both insert. This matches the existing codex/claude child behavior and the store's own documented best-effort semantics, so it's not a regression.

Summary

A large, unusually well-documented PR that meaningfully closes the parity gap between agy's terminal session and the web UI, with strong fixtures and tests behind each of the nine fixes. The mapper rework, psutil-based port discovery, cascade-ownership verification, and skill-source routing all hold up under review. However, there is one hard blocking bug: the new external_antigravity_subagent_start symbols are defined and re-exported but never imported into the modules that use them, so the sub-agent mirroring feature raises NameError at runtime and its own integration tests would fail. Fix the imports (and re-run the added test_sessions_endpoints.py antigravity cases to confirm green); the non-blocking items are edge-case hardening that can follow.

That notification was a late echo of the codex review I already collected and incorporated. Both cross-vendor reviews are complete and the final review has already been posted. No further action needed.


Automated review by Polly · workflow run

@KhoiFishGST

Copy link
Copy Markdown
Contributor Author

Thanks for reviewing @PattaraS! I will resolve your findings, and get back to you soon.

gstdevopsfleet pushed a commit to KhoiFishGST/omnigent that referenced this pull request Aug 5, 2026
Follow-up to the agy harness work, resolving reviewer feedback on omnigent-ai#3890.

- Bump the psutil floor to >=6: the connect-RPC port discovery calls
  Process.net_connections(), which 5.9 spells connections(). The
  AttributeError there is neither psutil.Error nor OSError, so it escaped
  the fallback instead of degrading to lsof.
- Seed the Global and Shared agy skill trees into the isolated Gemini dir
  alongside plugins, so the /skills menu cannot offer a skill agy would
  fail to expand. The other two sources need nothing: agy recreates its
  builtins under any --gemini_dir, and the workspace tree is not under it.
- Take a sub-agent's own nested mirrors down with it: a child's steps run
  the same path as the parent's, so a nested INVOKE_SUBAGENT registered a
  grandchild the reader's teardown drain never walked.
- Back the sub-agent quiescence window off after each veto instead of
  resetting it flat. agy answering "still running" can only veto the
  close, so a flat window re-asked every minute for the whole session.
- Fix two comments still attributing child exclusion to trajectoryType,
  which a subagent reports byte-identically to a root.
- Use a per-step chunk counter for planner delta indices. The forwarded
  byte offset moves backwards on a shorter post-moderation rewrite, and
  the server drops any chunk that does not outrank the last accepted one,
  so the closing final chunk was discarded and the block never closed.
- Prefer an exact match before the prefix scan in _arguments_from_body so
  a suffixed sibling key cannot shadow the argument that was asked for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
KhoiFishGST and others added 3 commits August 5, 2026 13:32
Follow-up to the agy harness work, resolving reviewer feedback on omnigent-ai#3890.

- Bump the psutil floor to >=6: the connect-RPC port discovery calls
  Process.net_connections(), which 5.9 spells connections(). The
  AttributeError there is neither psutil.Error nor OSError, so it escaped
  the fallback instead of degrading to lsof.
- Seed the Global and Shared agy skill trees into the isolated Gemini dir
  alongside plugins, so the /skills menu cannot offer a skill agy would
  fail to expand. The other two sources need nothing: agy recreates its
  builtins under any --gemini_dir, and the workspace tree is not under it.
- Take a sub-agent's own nested mirrors down with it: a child's steps run
  the same path as the parent's, so a nested INVOKE_SUBAGENT registered a
  grandchild the reader's teardown drain never walked.
- Back the sub-agent quiescence window off after each veto instead of
  resetting it flat. agy answering "still running" can only veto the
  close, so a flat window re-asked every minute for the whole session.
- Fix two comments still attributing child exclusion to trajectoryType,
  which a subagent reports byte-identically to a root.
- Use a per-step chunk counter for planner delta indices. The forwarded
  byte offset moves backwards on a shorter post-moderation rewrite, and
  the server drops any chunk that does not outrank the last accepted one,
  so the closing final chunk was discarded and the block never closed.
- Prefer an exact match before the prefix scan in _arguments_from_body so
  a suffixed sibling key cannot shadow the argument that was asked for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Khoi Nguyen <khoifish@goodstorytime.com>
Signed-off-by: Khoi Nguyen <khoifish@goodstorytime.com>

# Conflicts:
#	tests/runner/test_app_sessions_native_terminals_runtime.py
The sub-agent start path resolved its symbols through the sessions
wildcard imports, which main has since replaced with explicit blocks. The
references now fall through to NameError on the first
external_antigravity_subagent_start event.

Import each symbol from its owning module, matching how the codex
equivalents are already listed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Khoi Nguyen <khoifish@goodstorytime.com>
@KhoiFishGST

Copy link
Copy Markdown
Contributor Author

@PattaraS, I have fixed the issues you've mentioned, and also merged tip of main into the branch and resolved a conflict from the tip of main merge.

I re-ran with a fresh build, and everything works from my end (I tested subagents with agy, like before).

Unfortunately, psutil floor bumps uv.lock, which triggers the security gate failures. As far as I know, I think these happen on main as well, so not related to this PR.

Please let me know if you need anything else. Looking forward to seeing this land!

@KhoiFishGST
KhoiFishGST requested a review from PattaraS August 5, 2026 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-demo PR needs a demo screenshot or recording size/XL Pull request size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants